package profile;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;

import controller.ProfileController;

/**
 * Class used for profile chart panels.  This class holds the functionality for
 * selecting regions for the low pass filter.
 * 
 * @author cshanes
 *
 */
public class ProfileChartPanel extends ChartPanel {

    private static final long serialVersionUID = -3985788713008798681L;

    boolean useBuffer = false;
    Rectangle2D filterRectangle;
    Point2D filterPoint;
    private Paint filterRectangleFillPaint;
    private ProfileController controller;

	public ProfileChartPanel(JFreeChart chart, ProfileController controller, int width, int height) {
		 super(chart, width, height,
	                1600, 1200, 400, 300, 
	                false, //enable buffer (in my testing, enabling this made it noticeably slower)
	                true, //enable chart property editor via right click 
	                true, //enable save options via right click 
	                false, //enable print option via right click 
	                true, //enable zoom options via right click
	                true); //enables tooltips for chart

        // disable zooming so that we can replace that with selecting a range
        // for filtering
        setDomainZoomable(false);
        setRangeZoomable(false);
        
        controller.changeChartSelectionPresent(false);
        
        this.controller = controller;
        filterRectangleFillPaint = new Color(255, 150, 100, 45);
    }

    @Override
    public void mousePressed(MouseEvent e) {
        super.mousePressed(e);
        if (e.isPopupTrigger()) {
            return;
        }

        if (filterRectangle == null) {
            Rectangle2D screenDataArea = getScreenDataArea(e.getX(), e.getY());
            if (screenDataArea != null) {
                filterPoint = getPointInRectangle(e.getX(), e.getY(), screenDataArea);
            } else {
                filterPoint = null;
            }
        } else {
            eraseFilterRectangle();
        }
    }

    /**
     * Handles a 'mouse dragged' event.
     * 
     * @param e
     *            the mouse event.
     */
    @Override
    public void mouseDragged(MouseEvent e) {
        super.mouseDragged(e);

        // if no initial zoom point was set, ignore dragging...
        if (filterPoint == null) {
            return;
        }
        Graphics2D g2 = (Graphics2D) getGraphics();

        // erase the previous zoom rectangle (if any). We only need to do
        // this is we are using XOR mode, which we do when we're not using
        // the buffer (if there is a buffer, then at the end of this method we
        // just trigger a repaint)
        if (!useBuffer) {
            drawFilterRectangle(g2, true);
        }

        Rectangle2D scaledDataArea = getScreenDataArea((int) filterPoint.getX(),
                (int) filterPoint.getY());
        double xmax = Math.min(e.getX(), scaledDataArea.getMaxX());
        filterRectangle = new Rectangle2D.Double(filterPoint.getX(), scaledDataArea.getMinY(), xmax
                - filterPoint.getX(), scaledDataArea.getHeight());

        // Draw the new zoom rectangle...
        if (this.useBuffer) {
            repaint();
        } else {
            // with no buffer, we use XOR to draw the rectangle "over" the
            // chart...
            drawFilterRectangle(g2, true);
        }
        g2.dispose();

    }

    @Override
    public void mouseReleased(MouseEvent e) {
        super.mouseReleased(e);

        if (filterRectangle != null) {
            Rectangle2D screenDataArea = getScreenDataArea((int) filterPoint.getX(),
                    (int) filterPoint.getY());
            double maxX = screenDataArea.getMaxX();
            double x = filterPoint.getX();
            double w = Math.min(filterRectangle.getWidth(), maxX - filterPoint.getX());
            double x2 = x + w;

            //convert 2D cursor coordinates to chart values
            x = convertCoordinateToChartValue(x);
            w = convertCoordinateToChartValue(x2) - x;
            //System.out.println("X:" + x + " W:" + w);
            
            controller.changeChartSelectionPresent(true);
            controller.changeChartSelectionX((int) x);
            controller.changeChartSelectionWidth((int) w);
        }
    }

    private void eraseFilterRectangle() {
        controller.changeChartSelectionPresent(false);

        Graphics2D g2 = (Graphics2D) getGraphics();
        if (this.useBuffer) {
            repaint();
        } else {
            drawFilterRectangle(g2, true);
        }
        g2.dispose();
        filterPoint = null;
        filterRectangle = null;
    }

    /**
     * Draws filter rectangle (if present). The drawing is performed in XOR
     * mode, therefore when this method is called twice in a row, the second
     * call will completely restore the state of the canvas.
     * 
     * @param g2
     *            the graphics device.
     * @param xor
     *            use XOR for drawing?
     */
    private void drawFilterRectangle(Graphics2D g2, boolean xor) {
        if (filterRectangle != null) {
            if (xor) {
                // Set XOR mode to draw the zoom rectangle
                g2.setXORMode(Color.gray);
            }

            g2.setPaint(filterRectangleFillPaint);
            g2.fill(filterRectangle);
            if (xor) {
                // Reset to the default 'overwrite' mode
                g2.setPaintMode();
            }
        }
    }

    /**
     * Returns a point based on (x, y) but constrained to be within the bounds
     * of the given rectangle. This method could be moved to JCommon.
     * 
     * cshanes: I'm copy/pasting this from ChartPanel.java since the method is
     * private and should be moved somewhere else.
     * 
     * @param x
     *            the x-coordinate.
     * @param y
     *            the y-coordinate.
     * @param area
     *            the rectangle (<code>null</code> not permitted).
     * 
     * @return A point within the rectangle.
     */
    private Point2D getPointInRectangle(int x, int y, Rectangle2D area) {
        double xx = Math.max(area.getMinX(), Math.min(x, area.getMaxX()));
        double yy = Math.max(area.getMinY(), Math.min(y, area.getMaxY()));
        return new Point2D.Double(xx, yy);
    }
    
    private double convertCoordinateToChartValue(double value) {
        Rectangle2D area = getScreenDataArea();
        XYPlot plot = (XYPlot) getChart().getPlot();
        ValueAxis domainAxis = plot.getDomainAxis();
        double axisMin = domainAxis.getRange().getLowerBound();
        double axisMax = domainAxis.getRange().getUpperBound();
        double min = area.getX();
        double max = area.getMaxX();
        double result = axisMin + (value - min) / (max - min) * (axisMax - axisMin);
        return result;
    }
}
